package edu.gatech.cs2340.teamrocket.control.networking.node;

import java.util.List;
import java.util.ArrayList;
import edu.gatech.cs2340.teamrocket.control.networking.dialog.Dialog;
import edu.gatech.cs2340.teamrocket.control.networking.exception.TimeoutException;
import edu.gatech.cs2340.teamrocket.control.networking.message.ConnectionClosedMessage;
import edu.gatech.cs2340.teamrocket.control.networking.message.Message;
import edu.gatech.cs2340.teamrocket.control.networking.message.ServerFullMessage;
import edu.gatech.cs2340.teamrocket.control.networking.message.TimeoutMessage;
import edu.gatech.cs2340.teamrocket.control.networking.messagehandler.MessageHandler;

/**
 * A central Server that will have multiple Dialogs all of which are not enabled
 * to begin with.
 *
 * @author Joe Tacheron
 */
public class Server extends Node {

    private String password;
    private PortListeningRunnable portListener;
    private List<Dialog> confirmedDialogs;
    private int maxConnections;
    private int numConnections;

    /**
     *
     * @param handler default The MessageHandler.
     */
    public Server(MessageHandler handler){
        super(handler);
        this.confirmedDialogs = new ArrayList<Dialog>();
        this.maxConnections = Integer.MAX_VALUE;
    }

    /**
     *
     * @param handler default The MessageHandler.
     * @param maxConnections The maximum number of connections to accept.
     */
    public Server(MessageHandler handler, int maxConnections){
        super(handler);
        confirmedDialogs = new ArrayList<Dialog>();
        this.maxConnections = maxConnections;
    }

    /**
     * Start this Server.
     *
     * @param password The password for this Server.
     */
    public void start(String password){
        setRunning(true);
        this.password = password;
        portListener = new PortListeningRunnable(this);
        new Thread(portListener).start();
    }

    /**
     * Send a message to all open Dialogs and don't wait for responses.
     *
     * @param m The Message to send.
     */
    public void sendAll(Message m){
        for(Dialog d : confirmedDialogs){
            super.send(d, m);
        }
    }

    /**
     * Send a Message to all open Dialogs and wait on each for a response.
     *
     * @param m the Message to send.
     * @return An array of all Message replies.
     */
    public Message[] sendAllAndWait(Message m){
        Message[] replies = new Message[confirmedDialogs.size()];
        for(int i = 0; i < confirmedDialogs.size(); i++){
            try{
                replies[i] = super.sendAndWait(confirmedDialogs.get(i), m);
            }
            catch(TimeoutException e){
                replies[i] = new TimeoutMessage(m.getMessageID());
            }
        }
        return replies;
    }

    /**
     * Get the password for this Server.
     *
     * @return
     */
    public String getPassword(){
        return password;
    }

    /**
     * Add a new Dialog to this Server.
     *
     * @param d The new Dialog to add.
     */
    public synchronized void addNewDialog(Dialog d){
        if(numConnections < maxConnections){
            confirmedDialogs.add(d);
            numConnections++;
        }
        else{
            d.send(new ServerFullMessage());
            d.close();
        }
    }

    /**
     * Stop the Listener that is adding new Dialogs.
     */
    public void stopListening(){
        portListener.close();
    }

    /**
     * Close this server and Listener and send ConnectionClosedMessages to all
     * open Dialogs.
     */
    public void close(){
        setRunning(false);
        stopListening();
        for(Dialog dialog : confirmedDialogs){
            dialog.send(new ConnectionClosedMessage());
            dialog.close();
        }
    }
}